import {
  LOAD_ERROR,
  NOT_LOADED,
  NOT_BOOTSTRAPPED,
  NOT_MOUNTED,
  MOUNTED,
  LOADING_SOURCE_CODE,
  shouldBeActive,
  SKIP_BECAUSE_BROKEN
} from './app.helpers.js'
import { reroute } from '../navigations/reroute.js'
import { isInBrowser } from '../utils/runtime-environment'

const apps = [] // 用来存放所有的应用

/**
 * 维护应用的所有状态 
 * @param {*} appName 应用名字
 * @param {*} loadApp 加载的应用
 * @param {*} activeWhen 当激活时会调用 loadApp
 * @param {*} customProps 自定义属性
 */
export function registerApplication(appNameOrConfig, appOrLoadApp, activeWhen, customProps) {
  const registration = sanitizeArguments(
    appNameOrConfig,
    appOrLoadApp,
    activeWhen,
    customProps
  )
  apps.push({
    ...registration,
    status: NOT_LOADED,
    loadErrorTime: null
  })
  if (isInBrowser) {
    reroute() // 加载应用
  }
}

export function getAppChanges() {
  const appsToUnload = []
  const appsToUnmount = [] // 需要卸载的app
  const appsToLoad = [] // 需要被加载的app
  const appsToMount = [] // 需要被挂载的app

  // 我们会尝试 在LOAD_ERROR 200ms 后 再去下载应用
  const currentTime = new Date().getTime()
  apps.forEach(app => {
    // 需不需要被加载
    const appShouldBeActive = app.status !== SKIP_BECAUSE_BROKEN && shouldBeActive(app)

    switch(app.status) {
      case LOAD_ERROR:
        if (appShouldBeActive && currentTime - app.loadErrorTime >= 200) {
          appsToLoad.push(app)
        }
        break
      case NOT_LOADED:
      case LOADING_SOURCE_CODE:
        if (appShouldBeActive) {
          appsToLoad.push(app)
        }
        break
      case NOT_BOOTSTRAPPED:
      case NOT_MOUNTED:
        if (appShouldBeActive) {
          appsToMount.push(app)
        }
        break
      case MOUNTED:
        if (!appShouldBeActive) {
          appsToUnmount.push(app)
        }
        break 
      default:
        break
    }
  })
  return {
    appsToUnload,
    appsToUnmount,
    appsToLoad,
    appsToMount
  }
}

function sanitizeArguments (
  appNameOrConfig,
  appOrLoadApp,
  activeWhen,
  customProps
) {
  const usingObjectAPI = typeof appNameOrConfig === 'object'
  const registration = {
    name: null,
    loadApp: null,
    activeWhen: null,
    customProps: null
  }
  if (usingObjectAPI) {
    registration.name = appNameOrConfig.name
    registration.loadApp = appNameOrConfig.app
    registration.activeWhen = appNameOrConfig.activeWhen
    registration.customProps = appNameOrConfig.customProps
  } else {
    registration.name = appNameOrConfig
    registration.loadApp = appOrLoadApp
    registration.activeWhen = activeWhen
    registration.customProps = customProps
  }
  return registration
}